using Microsoft.Extensions.Logging;
using SeataNet.Core.Context;
using SeataNet.Core.Exceptions;
using SeataNet.Core.AT;
using SeataNet.Core.Database;
using SeataNet.Core.AT.UndoLog;
using System.Data;
using System.Data.Common;
using System.Text.Json;

namespace SeataNet.Core.AT
{
    /// <summary>
    /// AT模式连接代理
    ///
    /// 职责：
    /// 1. 代理数据库连接对象（DbConnection）
    /// 2. 拦截事务的开始操作，返回AT事务代理
    /// 3. 拦截命令的创建操作，返回AT命令代理
    /// 4. 管理连接的生命周期，确保资源正确释放
    ///
    /// 工作原理：
    /// - 继承自DbConnection，覆盖关键方法
    /// - 将目标连接（_targetConnection）包装为代理
    /// - BeginDbTransaction返回AtTransactionProxy
    /// - CreateDbCommand返回AtCommandProxy
    /// - 其他方法直接委托给目标连接
    ///
    /// 代理模式：
    /// ```
    /// 客户端 -> AtConnectionProxy -> 目标DbConnection -> 数据库
    ///           |
    ///           +-> AtTransactionProxy -> 目标DbTransaction
    ///           +-> AtCommandProxy -> 目标DbCommand
    /// ```
    ///
    /// 线程安全：
    /// - 每个连接代理实例对应一个目标连接
    /// - 通常在单线程中使用（一个连接不应跨线程共享）
    /// - _disposed字段用于防止重复释放
    ///
    /// 使用流程：
    /// 1. DataSourceProxy.CreateConnection() 创建AtConnectionProxy
    /// 2. 业务代码调用connection.Open()打开连接
    /// 3. 业务代码调用connection.BeginTransaction()开始事务（返回AtTransactionProxy）
    /// 4. 业务代码调用connection.CreateCommand()创建命令（返回AtCommandProxy）
    /// 5. 执行SQL时AtCommandProxy自动捕获前后镜像
    /// 6. 业务代码调用transaction.Commit()提交事务（保存undo_log）
    /// 7. 业务代码调用connection.Close()关闭连接
    /// 8. connection.Dispose()释放资源并注销连接
    ///
    /// 注意事项：
    /// - 必须在using语句中使用以确保资源释放
    /// - Dispose时会自动注销连接
    /// - 不支持嵌套事务（每个连接只能有一个事务）
    /// - 需要配置IDatabaseDialect和ITableMetaCache才能生成undo_log
    /// </summary>
    public class AtConnectionProxy : DbConnection
    {
        // 数据源代理，用于注册和注销连接
        private readonly IAtDataSourceProxy _dataSourceProxy;
        // AT事务管理器，用于管理全局事务和分支事务
        private readonly IAtTransactionManager _transactionManager;
        // 日志记录器
        private readonly ILogger<AtConnectionProxy> _logger;
        // 日志工厂，用于创建子组件的日志记录器
        private readonly ILoggerFactory _loggerFactory;
        // 目标数据库连接，真实的DbConnection对象
        private readonly DbConnection _targetConnection;
        // 数据库方言，用于生成不同数据库的SQL（如MySQL、SQL Server等）
        private readonly IDatabaseDialect _dialect;
        // 表元数据缓存，用于查询表结构信息（列名、主键等）
        private readonly ITableMetaCache _tableMetaCache;
        // 当前事务，AT事务代理对象
        private DbTransaction _transaction;
        // 是否已释放标志，防止重复释放
        private bool _disposed = false;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="dataSourceProxy">数据源代理，用于连接的注册和注销</param>
        /// <param name="transactionManager">AT事务管理器，用于管理分支事务</param>
        /// <param name="logger">日志记录器</param>
        /// <param name="loggerFactory">日志工厂，用于创建子组件的日志记录器</param>
        /// <param name="targetConnection">目标数据库连接，真实的DbConnection对象</param>
        /// <param name="dialect">数据库方言，可选，用于生成特定数据库的SQL</param>
        /// <param name="tableMetaCache">表元数据缓存，可选，用于查询表结构</param>
        public AtConnectionProxy(
            IAtDataSourceProxy dataSourceProxy,
            IAtTransactionManager transactionManager,
            ILogger<AtConnectionProxy> logger,
            ILoggerFactory loggerFactory,
            DbConnection targetConnection,
            IDatabaseDialect dialect = null,
            ITableMetaCache tableMetaCache = null)
        {
            _dataSourceProxy = dataSourceProxy ?? throw new ArgumentNullException(nameof(dataSourceProxy));
            _transactionManager = transactionManager ?? throw new ArgumentNullException(nameof(transactionManager));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
            _targetConnection = targetConnection ?? throw new ArgumentNullException(nameof(targetConnection));
            _dialect = dialect;
            _tableMetaCache = tableMetaCache;
        }

        /// <summary>
        /// 开始数据库事务
        ///
        /// 这是DbConnection的核心方法，被覆盖以返回AT事务代理。
        ///
        /// 执行流程：
        /// 1. 检查是否在全局事务上下文中（通过RootContext.GetXid()）
        /// 2. 在目标连接上开始本地事务（调用_targetConnection.BeginTransaction）
        /// 3. 将本地事务包装为AtTransactionProxy
        /// 4. 保存事务引用到_transaction字段
        /// 5. 返回AtTransactionProxy对象
        ///
        /// AT模式的两层事务：
        /// - 本地事务（_targetConnection.BeginTransaction）：
        ///   - 管理数据库层面的ACID特性
        ///   - 提供事务隔离级别
        ///   - 第一阶段提交后立即释放本地锁
        /// - 全局事务（通过AtTransactionProxy）：
        ///   - 管理undo_log的保存
        ///   - 协调分布式事务的两阶段提交
        ///   - 第二阶段通过TC协调
        ///
        /// 注意事项：
        /// - 无论是否在全局事务中，都返回AtTransactionProxy
        /// - 如果xid为空，AtTransactionProxy将跳过undo_log保存
        /// - 支持配置事务隔离级别
        /// - 一个连接同时只能有一个活跃事务
        /// </summary>
        /// <param name="isolationLevel">事务隔离级别</param>
        /// <returns>AT事务代理对象</returns>
        protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
        {
            _logger.LogDebug($"开始AT事务: IsolationLevel={isolationLevel}");

            // 检查是否在全局事务上下文中
            var xid = RootContext.GetXid();
            if (string.IsNullOrEmpty(xid))
            {
                // 不在全局事务中，使用本地事务
                _transaction = _targetConnection.BeginTransaction(isolationLevel);
                return new AtTransactionProxy(_transaction, xid, _transactionManager, _loggerFactory.CreateLogger<AtTransactionProxy>());
            }

            // 在全局事务中，创建AT事务代理
            _transaction = _targetConnection.BeginTransaction(isolationLevel);
            return new AtTransactionProxy(_transaction, xid, _transactionManager, _loggerFactory.CreateLogger<AtTransactionProxy>());
        }

        /// <summary>
        /// 关闭数据库连接
        ///
        /// 直接委托给目标连接的Close方法。
        ///
        /// 注意事项：
        /// - 关闭连接不会注销连接，只会改变连接状态
        /// - 连接可以再次Open重新打开
        /// - 资源释放应该使用Dispose方法
        /// </summary>
        public override void Close()
        {
            _targetConnection.Close();
        }

        /// <summary>
        /// 更改当前数据库
        ///
        /// 直接委托给目标连接的ChangeDatabase方法。
        ///
        /// 用途：
        /// - 切换到同一数据库服务器的另一个数据库
        /// - 通常用于多租户场景或动态数据库选择
        ///
        /// 注意事项：
        /// - 数据库名称不能为null
        /// - 连接必须处于Open状态
        /// </summary>
        /// <param name="databaseName">目标数据库名称</param>
        /// <exception cref="ArgumentNullException">数据库名称为null时抛出</exception>
        public override void ChangeDatabase(string databaseName)
        {
            if (databaseName == null)
            {
                throw new ArgumentNullException(nameof(databaseName));
            }
            _targetConnection.ChangeDatabase(databaseName);
        }

        /// <summary>
        /// 打开数据库连接
        ///
        /// 直接委托给目标连接的Open方法。
        ///
        /// 注意事项：
        /// - 连接必须在执行命令前打开
        /// - 重复调用Open会抛出异常
        /// - 建议使用using语句自动管理连接的打开和关闭
        /// </summary>
        public override void Open()
        {
            _targetConnection.Open();
        }

        /// <summary>
        /// 连接字符串属性
        ///
        /// 获取或设置目标连接的连接字符串。
        ///
        /// 注意事项：
        /// - 设置时值不能为null
        /// - 连接打开后不能更改连接字符串
        /// </summary>
        /// <exception cref="ArgumentNullException">设置时值为null抛出</exception>
        public override string ConnectionString
        {
            get => _targetConnection.ConnectionString;
            set => _targetConnection.ConnectionString = value ?? throw new ArgumentNullException(nameof(value));
        }

        /// <summary>
        /// 当前数据库名称
        ///
        /// 获取目标连接当前使用的数据库名称。
        /// </summary>
        public override string Database => _targetConnection.Database;

        /// <summary>
        /// 连接状态
        ///
        /// 获取目标连接的当前状态（Open、Closed等）。
        /// </summary>
        public override ConnectionState State => _targetConnection.State;

        /// <summary>
        /// 数据源名称
        ///
        /// 获取数据库服务器的名称或网络地址。
        /// </summary>
        public override string DataSource => _targetConnection.DataSource;

        /// <summary>
        /// 数据库服务器版本
        ///
        /// 获取数据库服务器的版本信息字符串。
        /// </summary>
        public override string ServerVersion => _targetConnection.ServerVersion;

        /// <summary>
        /// 创建数据库命令
        ///
        /// 这是DbConnection的核心方法，被覆盖以返回AT命令代理。
        ///
        /// 执行流程：
        /// 1. 调用目标连接的CreateCommand创建真实的DbCommand对象
        /// 2. 将真实命令包装为AtCommandProxy
        /// 3. 传入必要的依赖（连接代理、事务管理器、方言、元数据缓存）
        /// 4. 返回AtCommandProxy对象
        ///
        /// AtCommandProxy的作用：
        /// - 拦截ExecuteNonQuery等SQL执行方法
        /// - 在SQL执行前后捕获数据镜像（before/after image）
        /// - 生成undo_log并添加到事务中
        /// - 其他方法直接委托给真实命令
        ///
        /// 注意事项：
        /// - 返回的是AtCommandProxy，而非真实的DbCommand
        /// - 如果未配置dialect和tableMetaCache，将无法生成undo_log
        /// - 命令的Transaction属性会自动设置为当前事务
        /// </summary>
        /// <returns>AT命令代理对象</returns>
        protected override DbCommand CreateDbCommand()
        {
            var command = _targetConnection.CreateCommand();
            return new AtCommandProxy(
                command,
                this,
                _transactionManager,
                _loggerFactory.CreateLogger<AtCommandProxy>(),
                _dialect,
                _tableMetaCache);
        }

        /// <summary>
        /// 释放连接资源
        ///
        /// 执行流程：
        /// 1. 检查_disposed标志，防止重复释放
        /// 2. 如果disposing=true（显式调用Dispose）：
        ///    - 释放事务资源（_transaction.Dispose）
        ///    - 释放目标连接资源（_targetConnection.Dispose）
        ///    - 从数据源代理中注销连接（_dataSourceProxy.UnregisterConnection）
        /// 3. 设置_disposed=true
        /// 4. 调用基类的Dispose方法
        ///
        /// 调用时机：
        /// - using语句结束时自动调用
        /// - 显式调用connection.Dispose()
        /// - 析构函数调用时（disposing=false）
        ///
        /// 注意事项：
        /// - 必须调用Dispose以释放资源
        /// - Dispose后连接不能再使用
        /// - 注销连接会从数据源代理的字典中移除
        /// - 实现IDisposable模式，支持确定性释放
        /// </summary>
        /// <param name="disposing">是否显式调用Dispose</param>
        protected override void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }

            if (disposing)
            {
                _transaction?.Dispose();
                _targetConnection?.Dispose();
                _dataSourceProxy.UnregisterConnection(this);
            }

            _disposed = true;
            base.Dispose(disposing);
        }

        /// <summary>
        /// 目标连接属性
        ///
        /// 获取被代理的真实数据库连接对象。
        ///
        /// 用途：
        /// - AtCommandProxy需要访问真实连接来执行镜像捕获查询
        /// - 内部方法，仅在同一程序集内可见
        /// </summary>
        internal DbConnection TargetConnection => _targetConnection;
    }

    /// <summary>
    /// AT模式事务代理
    ///
    /// 职责：
    /// 1. 代理数据库事务对象（DbTransaction）
    /// 2. 拦截事务提交操作，保存undo_log到数据库
    /// 3. 拦截事务回滚操作
    /// 4. 管理undo_log列表
    ///
    /// 工作原理：
    /// - 继承自DbTransaction，覆盖Commit和Rollback方法
    /// - 维护_undoLogs列表，存储SQL执行过程中生成的undo_log
    /// - Commit时将所有undo_log批量写入undo_log表
    /// - Rollback时清空undo_log列表（因为本地事务回滚了）
    ///
    /// AT模式的两阶段提交：
    /// ```
    /// 第一阶段（Commit）：
    /// 1. 执行业务SQL（已在AtCommandProxy中完成）
    /// 2. 生成undo_log（已在AtCommandProxy中完成）
    /// 3. 提交本地事务（_targetTransaction.Commit）
    /// 4. 保存undo_log到数据库（SaveUndoLogs）
    /// 5. 释放本地锁，但保留TC的全局锁
    ///
    /// 第二阶段（由TC协调）：
    /// - 提交：TC通知删除undo_log，释放全局锁
    /// - 回滚：TC通知根据undo_log生成反向SQL并执行
    /// ```
    ///
    /// 线程安全：
    /// - 每个事务实例对应一个目标事务
    /// - 通常在单线程中使用
    /// - _disposed字段用于防止重复释放
    ///
    /// 使用流程：
    /// 1. connection.BeginTransaction()创建AtTransactionProxy
    /// 2. 执行SQL时AtCommandProxy调用AddUndoLog添加undo_log
    /// 3. transaction.Commit()提交事务并保存undo_log
    /// 4. transaction.Dispose()释放资源
    ///
    /// 注意事项：
    /// - 如果xid为空，跳过undo_log保存（不在全局事务中）
    /// - SaveUndoLogs是简化实现，实际需要写入undo_log表
    /// - undo_log表结构需要预先创建
    /// - 支持事务的Rollback操作
    /// </summary>
    public class AtTransactionProxy : DbTransaction
    {
        // 目标事务，真实的DbTransaction对象
        private readonly DbTransaction _targetTransaction;
        // 全局事务ID，从RootContext获取，可能为空（不在全局事务中）
        private readonly string _xid;
        // AT事务管理器，用于管理分支事务
        private readonly IAtTransactionManager _transactionManager;
        // 日志记录器
        private readonly ILogger<AtTransactionProxy> _logger;
        // 是否已释放标志，防止重复释放
        private bool _disposed = false;
        // Undo日志列表，存储SQL执行过程中生成的undo_log
        private List<AtUndoLog> _undoLogs = new List<AtUndoLog>();

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="targetTransaction">目标事务，真实的DbTransaction对象</param>
        /// <param name="xid">全局事务ID，可能为空（不在全局事务中）</param>
        /// <param name="transactionManager">AT事务管理器</param>
        /// <param name="logger">日志记录器</param>
        public AtTransactionProxy(
            DbTransaction targetTransaction,
            string xid,
            IAtTransactionManager transactionManager,
            ILogger<AtTransactionProxy> logger)
        {
            _targetTransaction = targetTransaction;
            _xid = xid;
            _transactionManager = transactionManager;
            _logger = logger;
        }

        /// <summary>
        /// 提交事务
        ///
        /// AT模式第一阶段提交的核心方法。
        ///
        /// 执行流程：
        /// 1. 检查是否在全局事务中（_xid是否为空）
        /// 2. 如果不在全局事务中，直接提交本地事务并返回
        /// 3. 如果在全局事务中：
        ///    - 提交本地事务（_targetTransaction.Commit）
        ///    - 调用SaveUndoLogs保存所有undo_log到数据库
        ///    - 记录提交成功日志
        ///
        /// AT模式第一阶段提交的特点：
        /// - 本地事务立即提交，释放本地锁
        /// - 数据修改对其他事务可见（脏读）
        /// - 通过TC的全局锁控制并发
        /// - undo_log保存在同一个本地事务中
        ///
        /// 注意事项：
        /// - 本地事务提交后，数据修改已持久化
        /// - 如果后续SaveUndoLogs失败，需要人工介入
        /// - undo_log保存失败会抛出异常
        /// - 提交后不能再执行SQL
        /// </summary>
        /// <exception cref="Exception">提交失败时抛出</exception>
        public override void Commit()
        {
            _logger.LogDebug($"提交AT事务: XID={_xid}");

            try
            {
                // 如果不在全局事务中，直接提交本地事务
                if (string.IsNullOrEmpty(_xid))
                {
                    _targetTransaction.Commit();
                    return;
                }

                // 在全局事务中，提交本地事务并保存回滚日志
                _targetTransaction.Commit();
                
                // 保存回滚日志到数据库
                SaveUndoLogs();
                
                _logger.LogDebug($"AT事务已提交: XID={_xid}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"提交AT事务失败: XID={_xid}");
                throw;
            }
        }

        /// <summary>
        /// 回滚事务
        ///
        /// 执行流程：
        /// 1. 检查是否在全局事务中（_xid是否为空）
        /// 2. 如果不在全局事务中，直接回滚本地事务并返回
        /// 3. 如果在全局事务中，回滚本地事务
        ///
        /// 注意事项：
        /// - 回滚会撤销所有未提交的数据修改
        /// - undo_log列表会被自动清空（本地事务回滚了，无需保存）
        /// - 回滚后不能再执行SQL
        /// - 全局事务的回滚由TC协调，在第二阶段执行
        /// </summary>
        /// <exception cref="Exception">回滚失败时抛出</exception>
        public override void Rollback()
        {
            _logger.LogDebug($"回滚AT事务: XID={_xid}");

            try
            {
                // 如果不在全局事务中，直接回滚本地事务
                if (string.IsNullOrEmpty(_xid))
                {
                    _targetTransaction.Rollback();
                    return;
                }

                // 在全局事务中，回滚本地事务
                _targetTransaction.Rollback();

                _logger.LogDebug($"AT事务已回滚: XID={_xid}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"回滚AT事务失败: XID={_xid}");
                throw;
            }
        }

        /// <summary>
        /// 事务关联的数据库连接
        ///
        /// 获取目标事务关联的数据库连接。
        /// </summary>
        protected override DbConnection DbConnection => _targetTransaction.Connection;

        /// <summary>
        /// 事务隔离级别
        ///
        /// 获取目标事务的隔离级别。
        /// </summary>
        public override IsolationLevel IsolationLevel => _targetTransaction.IsolationLevel;

        /// <summary>
        /// 释放事务资源
        ///
        /// 执行流程：
        /// 1. 检查_disposed标志，防止重复释放
        /// 2. 如果disposing=true，释放目标事务资源
        /// 3. 设置_disposed=true
        /// 4. 调用基类的Dispose方法
        ///
        /// 注意事项：
        /// - 释放前应该先Commit或Rollback
        /// - 未提交的事务会被自动回滚
        /// </summary>
        /// <param name="disposing">是否显式调用Dispose</param>
        protected override void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }

            if (disposing)
            {
                _targetTransaction?.Dispose();
            }

            _disposed = true;
            base.Dispose(disposing);
        }

        /// <summary>
        /// 添加Undo日志
        ///
        /// 由AtCommandProxy调用，将生成的undo_log添加到列表中。
        ///
        /// 执行流程：
        /// 1. 将undo_log添加到_undoLogs列表
        /// 2. 提交时批量保存所有undo_log
        ///
        /// 调用时机：
        /// - AtCommandProxy执行SQL后生成undo_log
        /// - 每个SQL操作可能产生一条undo_log
        ///
        /// 注意事项：
        /// - 此方法是internal的，仅在同一程序集内可见
        /// - 列表不是线程安全的，应该在单线程中使用
        /// </summary>
        /// <param name="undoLog">待添加的undo日志</param>
        internal void AddUndoLog(AtUndoLog undoLog)
        {
            _undoLogs.Add(undoLog);
        }

        /// <summary>
        /// 保存Undo日志到数据库
        ///
        /// 将所有undo_log批量写入undo_log表。
        ///
        /// 执行流程：
        /// 1. 遍历_undoLogs列表
        /// 2. 为每条undo_log生成INSERT语句
        /// 3. 执行INSERT语句将undo_log保存到数据库
        /// 4. 记录保存日志
        ///
        /// undo_log表结构（示例）：
        /// ```sql
        /// CREATE TABLE undo_log (
        ///   id BIGINT PRIMARY KEY AUTO_INCREMENT,
        ///   xid VARCHAR(100) NOT NULL,
        ///   branch_id BIGINT NOT NULL,
        ///   table_name VARCHAR(255) NOT NULL,
        ///   sql_type VARCHAR(50),
        ///   before_image TEXT,
        ///   after_image TEXT,
        ///   create_time DATETIME,
        ///   INDEX idx_xid (xid),
        ///   INDEX idx_branch_id (branch_id)
        /// );
        /// ```
        ///
        /// 注意事项：
        /// - 这是简化实现，实际需要根据具体数据库实现
        /// - before_image和after_image需要序列化为JSON
        /// - 保存失败会抛出异常，需要事务回滚
        /// - undo_log保存在同一个本地事务中，保证原子性
        /// </summary>
        private void SaveUndoLogs()
        {
            // 这里应该将回滚日志保存到数据库
            // 在实际实现中，需要创建undo_log表并插入记录
            _logger.LogDebug($"保存回滚日志: Count={_undoLogs.Count}, XID={_xid}");

            // 示例代码，实际实现需要根据具体数据库类型实现
            foreach (var undoLog in _undoLogs)
            {
                _logger.LogDebug($"保存回滚日志: Table={undoLog.TableName}, XID={undoLog.Xid}, BranchId={undoLog.BranchId}");
            }
        }
    }

    /// <summary>
    /// AT模式命令代理
    ///
    /// 职责：
    /// 1. 代理数据库命令对象（DbCommand）
    /// 2. 拦截ExecuteNonQuery等SQL执行方法
    /// 3. 在SQL执行前后捕获数据镜像（before/after image）
    /// 4. 生成undo_log并添加到事务中
    ///
    /// 工作原理：
    /// - 继承自DbCommand，覆盖ExecuteNonQuery方法
    /// - 使用IDatabaseDialect识别SQL类型（INSERT、UPDATE、DELETE等）
    /// - 使用ITableMetaCache获取表结构信息
    /// - 在SQL执行前执行before image查询
    /// - 执行真实的业务SQL
    /// - 在SQL执行后执行after image查询
    /// - 将before/after image封装为undo_log
    /// - 调用AtTransactionProxy.AddUndoLog添加到事务
    ///
    /// 镜像捕获流程：
    /// ```
    /// 1. 解析SQL，识别类型（UPDATE、DELETE等）
    /// 2. 获取表元数据（主键、列信息）
    /// 3. 生成before image SQL（SELECT * FROM table WHERE pk IN (...)）
    /// 4. 执行before image SQL，保存查询结果
    /// 5. 执行业务SQL
    /// 6. 生成after image SQL（SELECT * FROM table WHERE pk IN (...)）
    /// 7. 执行after image SQL，保存查询结果
    /// 8. 创建undo_log记录
    /// 9. 添加到事务的undo_logs列表
    /// ```
    ///
    /// 线程安全：
    /// - 每个命令实例对应一个目标命令
    /// - 通常在单线程中使用
    ///
    /// 使用流程：
    /// 1. connection.CreateCommand()创建AtCommandProxy
    /// 2. 设置CommandText和Parameters
    /// 3. command.ExecuteNonQuery()执行SQL（自动捕获镜像）
    /// 4. command.Dispose()释放资源
    ///
    /// 注意事项：
    /// - SELECT语句不会生成undo_log
    /// - 需要配置IDatabaseDialect和ITableMetaCache
    /// - 未配置时会记录警告并跳过undo_log生成
    /// - 镜像捕获会增加SQL执行次数（before + 业务 + after）
    /// - 性能开销：每次DML操作额外执行2次SELECT查询
    /// </summary>
    public class AtCommandProxy : DbCommand
    {
        // 目标命令，真实的DbCommand对象
        private readonly DbCommand _targetCommand;
        // 连接代理，用于访问真实连接以执行镜像捕获查询
        private readonly AtConnectionProxy _connectionProxy;
        // AT事务管理器，用于管理分支事务
        private readonly IAtTransactionManager _transactionManager;
        // 日志记录器
        private readonly ILogger<AtCommandProxy> _logger;
        // 数据库方言，用于生成不同数据库的SQL（如MySQL、SQL Server等）
        private readonly IDatabaseDialect _dialect;
        // 表元数据缓存，用于查询表结构信息（列名、主键等）
        private readonly ITableMetaCache _tableMetaCache;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="targetCommand">目标命令，真实的DbCommand对象</param>
        /// <param name="connectionProxy">连接代理，用于访问真实连接</param>
        /// <param name="transactionManager">AT事务管理器</param>
        /// <param name="logger">日志记录器</param>
        /// <param name="dialect">数据库方言，可选，用于生成特定数据库的SQL</param>
        /// <param name="tableMetaCache">表元数据缓存，可选，用于查询表结构</param>
        public AtCommandProxy(
            DbCommand targetCommand,
            AtConnectionProxy connectionProxy,
            IAtTransactionManager transactionManager,
            ILogger<AtCommandProxy> logger,
            IDatabaseDialect dialect = null,
            ITableMetaCache tableMetaCache = null)
        {
            _targetCommand = targetCommand ?? throw new ArgumentNullException(nameof(targetCommand));
            _connectionProxy = connectionProxy ?? throw new ArgumentNullException(nameof(connectionProxy));
            _transactionManager = transactionManager ?? throw new ArgumentNullException(nameof(transactionManager));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _dialect = dialect;
            _tableMetaCache = tableMetaCache;
        }

        /// <summary>
        /// 取消命令执行
        ///
        /// 直接委托给目标命令的Cancel方法。
        ///
        /// 注意事项：
        /// - 只能取消正在执行的命令
        /// - 取消不保证立即生效
        /// </summary>
        public override void Cancel()
        {
            _targetCommand.Cancel();
        }

        /// <summary>
        /// 执行非查询SQL命令（INSERT、UPDATE、DELETE等）
        ///
        /// 这是AT模式自动镜像捕获的入口方法。
        ///
        /// 执行流程：
        /// 1. 从RootContext获取当前全局事务ID（XID）
        /// 2. 如果不在全局事务中（XID为空）：
        ///    - 直接执行SQL，不捕获镜像
        ///    - 返回受影响的行数
        /// 3. 如果没有配置数据库方言或表元数据缓存：
        ///    - 记录警告日志
        ///    - 直接执行SQL，不生成undo_log
        ///    - 返回受影响的行数
        /// 4. 如果在全局事务中且配置完整：
        ///    - 调用ExecuteNonQueryWithImageCapture进行镜像捕获
        ///    - 自动生成before/after image
        ///    - 生成undo_log并添加到事务
        ///    - 返回受影响的行数
        ///
        /// 调用时机：
        /// - 执行INSERT、UPDATE、DELETE等DML语句时
        /// - 由ORM框架或业务代码自动调用
        ///
        /// 注意事项：
        /// - SELECT语句应使用ExecuteReader或ExecuteScalar
        /// - 镜像捕获会额外执行2次SELECT查询（before + after）
        /// - 未配置dialect或tableMetaCache时不会生成undo_log
        /// </summary>
        /// <returns>受影响的行数</returns>
        public override int ExecuteNonQuery()
        {
            var xid = RootContext.GetXid();

            // 如果不在全局事务中，直接执行命令
            if (string.IsNullOrEmpty(xid))
            {
                return _targetCommand.ExecuteNonQuery();
            }

            // 如果没有配置数据库方言，记录警告并直接执行
            if (_dialect == null || _tableMetaCache == null)
            {
                _logger.LogWarning("未配置数据库方言或表元数据缓存，将不会记录undo log");
                return _targetCommand.ExecuteNonQuery();
            }

            // 使用数据库方言记录执行前后的数据状态
            var result = ExecuteNonQueryWithImageCapture(xid).GetAwaiter().GetResult();
            return result;
        }

        /// <summary>
        /// 执行带镜像捕获的非查询SQL命令
        ///
        /// 这是AT模式镜像捕获的核心实现方法。
        ///
        /// 执行流程：
        /// 1. 使用数据库方言的SqlRecognizer识别SQL类型和表名
        /// 2. 如果是SELECT_FOR_UPDATE语句，直接执行不捕获镜像
        /// 3. 从表元数据缓存获取表结构信息（主键、列等）
        /// 4. 执行before image捕获：
        ///    - 使用dialect.BuildBeforeImageSql生成查询SQL
        ///    - 调用CaptureTableRecordsAsync执行查询
        ///    - 保存修改前的数据快照
        /// 5. 执行业务SQL（_targetCommand.ExecuteNonQuery）
        /// 6. 执行after image捕获：
        ///    - 使用dialect.BuildAfterImageSql生成查询SQL
        ///    - 调用CaptureTableRecordsAsync执行查询
        ///    - 保存修改后的数据快照
        /// 7. 创建UndoLogRecord对象，包含表名、SQL类型、before/after镜像
        /// 8. 如果当前事务是AtTransactionProxy：
        ///    - 创建AtUndoLog对象
        ///    - 设置XID、BranchId、TableName等信息
        ///    - 调用atTransaction.AddUndoLog添加到事务的undo_logs列表
        /// 9. 返回受影响的行数
        ///
        /// 注意事项：
        /// - before/after image SQL由数据库方言生成，支持不同数据库
        /// - BeforeImage用于回滚时恢复数据
        /// - AfterImage用于脏写检测（检查数据是否被其他事务修改）
        /// - 镜像捕获失败会抛出异常并回滚事务
        /// - BranchId在此处设置为0，实际应在分支注册时设置
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <returns>受影响的行数</returns>
        /// <exception cref="Exception">SQL识别失败、镜像捕获失败或undo_log生成失败时抛出</exception>
        private async Task<int> ExecuteNonQueryWithImageCapture(string xid)
        {
            try
            {
                // 识别SQL
                var recognizer = _dialect.GetSqlRecognizer(_targetCommand.CommandText);
                var recognizeResult = recognizer.Recognize();

                if (recognizeResult.SqlType == SqlType.SELECT_FOR_UPDATE)
                {
                    // SELECT语句直接执行
                    return _targetCommand.ExecuteNonQuery();
                }

                // 获取表元数据
                var tableMeta = await _tableMetaCache.GetTableMetaAsync(
                    _connectionProxy.TargetConnection,
                    recognizeResult.TableName);

                // 捕获前镜像
                TableRecords beforeImage = null;
                var beforeSql = _dialect.BuildBeforeImageSql(recognizeResult, tableMeta);
                if (!string.IsNullOrEmpty(beforeSql))
                {
                    beforeImage = await CaptureTableRecordsAsync(beforeSql, tableMeta);
                }

                // 执行实际操作
                var affectedRows = _targetCommand.ExecuteNonQuery();

                // 捕获后镜像
                TableRecords afterImage = null;
                var afterSql = _dialect.BuildAfterImageSql(recognizeResult, tableMeta);
                if (!string.IsNullOrEmpty(afterSql))
                {
                    afterImage = await CaptureTableRecordsAsync(afterSql, tableMeta);
                }

                // 创建undo log记录
                var undoLogRecord = new UndoLogRecord
                {
                    TableName = recognizeResult.TableName,
                    SqlType = recognizeResult.SqlType,
                    BeforeImage = beforeImage,
                    AfterImage = afterImage
                };

                // 将回滚日志添加到事务中
                if (_targetCommand.Transaction is AtTransactionProxy atTransaction)
                {
                    var undoLog = new AtUndoLog
                    {
                        Xid = xid,
                        BranchId = 0, // 分支ID在注册分支事务时设置
                        TableName = recognizeResult.TableName,
                        Sql = _targetCommand.CommandText,
                        BeforeImage = new Dictionary<string, object>(), // 简化版本，实际应使用undoLogRecord
                        AfterImage = new Dictionary<string, object>(),
                        CreateTime = DateTime.UtcNow
                    };
                    atTransaction.AddUndoLog(undoLog);
                }
                else if (_targetCommand.Transaction != null)
                {
                    _logger.LogWarning("事务类型不是AtTransactionProxy，无法添加回滚日志");
                }

                return affectedRows;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"执行AT命令失败: XID={xid}, SQL={_targetCommand.CommandText}");
                throw;
            }
        }

        /// <summary>
        /// 捕获表记录（镜像捕获的实际执行方法）
        ///
        /// 执行镜像捕获SQL查询，将查询结果封装为TableRecords对象。
        ///
        /// 执行流程：
        /// 1. 创建TableRecords对象，设置表名和表元数据
        /// 2. 使用真实连接（_connectionProxy.TargetConnection）创建查询命令
        /// 3. 设置命令的SQL和事务（必须在同一个事务中）
        /// 4. 复制业务命令的参数到查询命令（保持参数值一致）
        /// 5. 执行查询并读取结果：
        ///    - 遍历每一行数据
        ///    - 遍历每一列，读取列名、列值、列类型
        ///    - 创建Field对象并添加到Row中
        ///    - 将Row添加到TableRecords中
        /// 6. 返回TableRecords对象
        ///
        /// 使用场景：
        /// - ExecuteNonQueryWithImageCapture调用此方法获取before image
        /// - ExecuteNonQueryWithImageCapture调用此方法获取after image
        ///
        /// 注意事项：
        /// - 必须在同一个事务中执行镜像查询
        /// - 参数必须从业务命令复制，确保查询条件一致
        /// - NULL值会被保存为null（通过reader.IsDBNull判断）
        /// - 列类型通过reader.GetFieldType获取
        /// </summary>
        /// <param name="sql">镜像捕获SQL（由DatabaseDialect生成）</param>
        /// <param name="tableMeta">表元数据，包含表结构信息</param>
        /// <returns>捕获的表记录，包含所有行和列的数据</returns>
        private async Task<TableRecords> CaptureTableRecordsAsync(string sql, TableMeta tableMeta)
        {
            var tableRecords = new TableRecords
            {
                TableName = tableMeta.TableName,
                TableMeta = tableMeta
            };

            using (var command = _connectionProxy.TargetConnection.CreateCommand())
            {
                command.CommandText = sql;
                command.Transaction = _targetCommand.Transaction;

                // 复制参数
                foreach (DbParameter param in _targetCommand.Parameters)
                {
                    var newParam = command.CreateParameter();
                    newParam.ParameterName = param.ParameterName;
                    newParam.Value = param.Value;
                    newParam.DbType = param.DbType;
                    command.Parameters.Add(newParam);
                }

                using (var reader = await command.ExecuteReaderAsync())
                {
                    while (await reader.ReadAsync())
                    {
                        var row = new Row();
                        for (int i = 0; i < reader.FieldCount; i++)
                        {
                            var fieldName = reader.GetName(i);
                            var fieldValue = reader.IsDBNull(i) ? null : reader.GetValue(i);
                            var fieldType = reader.GetFieldType(i).Name;

                            row.Fields.Add(new Field
                            {
                                Name = fieldName,
                                Value = fieldValue,
                                Type = fieldType
                            });
                        }
                        tableRecords.Rows.Add(row);
                    }
                }
            }

            return tableRecords;
        }

        /// <summary>
        /// 执行查询并返回结果集第一行第一列的值
        ///
        /// 直接委托给目标命令的ExecuteScalar方法。
        ///
        /// 使用场景：
        /// - 执行聚合查询（COUNT、SUM、MAX等）
        /// - 获取单个标量值
        ///
        /// 注意事项：
        /// - 不会捕获镜像或生成undo_log
        /// - 适用于不修改数据的查询
        /// </summary>
        /// <returns>结果集第一行第一列的值，如果结果集为空则返回null</returns>
        public override object ExecuteScalar()
        {
            return _targetCommand.ExecuteScalar();
        }

        /// <summary>
        /// 执行查询并返回数据读取器
        ///
        /// 直接委托给目标命令的ExecuteReader方法。
        ///
        /// 使用场景：
        /// - 执行SELECT查询
        /// - 遍历查询结果集
        ///
        /// 注意事项：
        /// - 不会捕获镜像或生成undo_log
        /// - 返回的DbDataReader必须显式关闭
        /// - 使用using语句确保资源释放
        /// </summary>
        /// <param name="behavior">命令执行行为标志</param>
        /// <returns>数据读取器，用于遍历查询结果</returns>
        protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
        {
            return _targetCommand.ExecuteReader(behavior);
        }

        /// <summary>
        /// 准备命令（预编译SQL）
        ///
        /// 直接委托给目标命令的Prepare方法。
        ///
        /// 使用场景：
        /// - 需要多次执行同一SQL时
        /// - 提高执行性能
        ///
        /// 注意事项：
        /// - 调用前必须设置CommandText
        /// - 调用前必须设置所有参数的类型
        /// </summary>
        public override void Prepare()
        {
            _targetCommand.Prepare();
        }

        /// <summary>
        /// 创建命令参数
        ///
        /// 直接委托给目标命令的CreateParameter方法。
        ///
        /// 使用场景：
        /// - 创建参数化查询的参数
        /// - 避免SQL注入
        ///
        /// 返回值：
        /// - 返回目标命令的参数对象
        /// </summary>
        /// <returns>新创建的参数对象</returns>
        protected override DbParameter CreateDbParameter()
        {
            return _targetCommand.CreateParameter();
        }

        /// <summary>
        /// 命令文本属性（SQL语句）
        ///
        /// 获取或设置要执行的SQL语句。
        ///
        /// 注意事项：
        /// - 设置时值不能为null
        /// - SQL会被数据库方言解析以识别类型和表名
        /// </summary>
        /// <exception cref="ArgumentNullException">设置时值为null抛出</exception>
        public override string CommandText
        {
            get => _targetCommand.CommandText;
            set => _targetCommand.CommandText = value ?? throw new ArgumentNullException(nameof(value));
        }

        /// <summary>
        /// 命令超时时间（秒）
        ///
        /// 获取或设置命令执行的超时时间。
        ///
        /// 注意事项：
        /// - 默认值通常为30秒
        /// - 超时后命令会被取消并抛出异常
        /// - 设置为0表示无限等待（不推荐）
        /// </summary>
        public override int CommandTimeout
        {
            get => _targetCommand.CommandTimeout;
            set => _targetCommand.CommandTimeout = value;
        }

        /// <summary>
        /// 命令类型
        ///
        /// 获取或设置命令的类型（Text、StoredProcedure等）。
        ///
        /// 可选值：
        /// - Text：SQL语句（默认）
        /// - StoredProcedure：存储过程
        /// - TableDirect：表名（仅部分提供程序支持）
        /// </summary>
        public override CommandType CommandType
        {
            get => _targetCommand.CommandType;
            set => _targetCommand.CommandType = value;
        }

        /// <summary>
        /// 更新行源
        ///
        /// 获取或设置命令结果如何应用到DataRow。
        ///
        /// 可选值：
        /// - None：不更新
        /// - OutputParameters：输出参数
        /// - FirstReturnedRecord：第一条返回记录
        /// - Both：输出参数和第一条返回记录
        /// </summary>
        public override UpdateRowSource UpdatedRowSource
        {
            get => _targetCommand.UpdatedRowSource;
            set => _targetCommand.UpdatedRowSource = value;
        }

        /// <summary>
        /// 命令关联的数据库连接
        ///
        /// 获取或设置命令使用的数据库连接。
        ///
        /// 注意事项：
        /// - 返回的是AtConnectionProxy，而非真实连接
        /// - 不允许更改连接（抛出NotSupportedException）
        /// </summary>
        /// <exception cref="NotSupportedException">设置时抛出，不允许更改AT代理连接</exception>
        protected override DbConnection DbConnection
        {
            get => _connectionProxy;
            set => throw new NotSupportedException("不能更改AT代理连接");
        }

        /// <summary>
        /// 命令参数集合
        ///
        /// 获取命令的参数集合，用于参数化查询。
        ///
        /// 使用场景：
        /// - 添加查询参数
        /// - 避免SQL注入
        /// - 支持参数重用
        /// </summary>
        protected override DbParameterCollection DbParameterCollection => _targetCommand.Parameters;

        /// <summary>
        /// 命令关联的事务
        ///
        /// 获取或设置命令所在的事务。
        ///
        /// 注意事项：
        /// - 设置时值不能为null
        /// - 必须在同一个事务中执行命令和镜像捕获
        /// - 事务提交或回滚后不能再使用该命令
        /// </summary>
        /// <exception cref="ArgumentNullException">设置时值为null抛出</exception>
        protected override DbTransaction DbTransaction
        {
            get => _targetCommand.Transaction;
            set => _targetCommand.Transaction = value ?? throw new ArgumentNullException(nameof(value));
        }

        /// <summary>
        /// 设计时可见性
        ///
        /// 获取或设置命令对象是否在设计时可见。
        ///
        /// 注意事项：
        /// - 通常在可视化设计器中使用
        /// - 运行时无影响
        /// </summary>
        public override bool DesignTimeVisible
        {
            get => _targetCommand.DesignTimeVisible;
            set => _targetCommand.DesignTimeVisible = value;
        }

        /// <summary>
        /// 释放命令资源
        ///
        /// 执行流程：
        /// 1. 检查disposing标志
        /// 2. 如果disposing=true，释放目标命令资源
        /// 3. 调用基类的Dispose方法
        ///
        /// 注意事项：
        /// - 必须调用Dispose以释放资源
        /// - Dispose后命令不能再使用
        /// - 建议使用using语句自动管理资源
        /// </summary>
        /// <param name="disposing">是否显式调用Dispose</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _targetCommand?.Dispose();
            }

            base.Dispose(disposing);
        }
    }
}